﻿using System;
using System.Collections.Generic;
using System.Text;
using FySystem.Extension;
using FySystem.Secure;

namespace FySystem.Net.FyNetConnection.Src.SubPacking
{
    internal class SubPackingWithFyModeV1
    {
        /// <summary>
        /// 按照第一版本的专属协议进行分包（有缺陷，粘包没有处理好，只能用于一问一答的情景，单消息长度不能超过10M）
        /// </summary>
        /// <param name="bytesRead"></param>
        /// <param name="stateObject"></param>
        /// <returns></returns>
        internal static List<byte[]> SubPacking(int bytesRead, StateObjectBase stateObject)
        {
            List<byte[]> res = null;
            try
            {
                List<byte> bytes = new List<byte>();

                //加入之前未结束的字段
                if (stateObject.stream != null)
                {
                    if (stateObject.stream.Length > 0)
                        bytes.AddRange(stateObject.stream);
                }

                //加入当前收到的数据
                for (var i = 0; i < bytesRead; i++)
                    bytes.Add(stateObject.Buffer[i]);

                //校验帧头
                if (bytes.Count <= 2)
                {
                    stateObject.stream = bytes.ToArray();
                    return res;
                }

                byte[] byteFirst = new byte[] { 0x20, 0x08 };
                int endIndex = 0;
                int index = -1;
                List<byte[]> msgs = new List<byte[]>();                 //分割出来的消息数组

                while (true)
                {
                    int mIndex = bytes.IndexOf(endIndex, byteFirst);
                    if (index >= 0 && mIndex <= index)
                    {
                        //说明前两个字节是2008，但后面的数据可能是不完整的帧，也可能是不合法帧数据，越过2008两个字节，继续找下一个2008
                        mIndex = bytes.IndexOf(index + 2, byteFirst);
                    }
                    index = mIndex;

                    if (index == -1)
                    {
                        /* 一条指令不可能太长，如果超过10M了还没找到一条有效的指令，就丢弃，返回数据的时候一条数据一个包
                         * 这里有个缺陷，如果有人不停的发格式不正确的包，程序会将所有不正确的数据积攒起来，直到超过10M，
                         * 如果多开点线程不停的发，程序就会爆掉，突然明白为什么数据库都会限制最大连接数了
                         * */
                        if (bytes.Count - index >= 10485760)
                            stateObject.stream = null;

                        byte[] partFrame = new byte[bytes.Count - endIndex];
                        for (int i = endIndex; i < bytes.Count; i++)
                        {
                            partFrame[i - endIndex] = bytes[i];
                        }
                        stateObject.stream = partFrame;

                        break;
                    }

                    List<byte> frame = SeparateFrame(bytes, index, ref endIndex);
                    if (frame != null)
                    {
                        msgs.Add(frame.ToArray());
                        if (endIndex >= bytes.Count)
                        {
                            stateObject.stream = null;
                            break;
                        }
                    }
                }

                if (msgs.Count > 0)
                    res = msgs;
            }
            catch (Exception)
            {
                throw;
            }
            return res;
        }

        /// <summary>
        /// 从指定位置开始，找出帧数据，如果数据校验出来不是帧数据，则返回空
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        internal static List<byte> SeparateFrame(List<byte> bytes, int startIndex, ref int endIndex)
        {
            List<byte> res = null;
            try
            {
                if (bytes == null)
                    return res;
                if (startIndex > bytes.Count - 8)
                    return res;
                if (bytes[0] != 0x20 && bytes[1] != 0x08)
                    return res;

                //两字节数据长度
                byte[] byteArr = new byte[4];
                byteArr[0] = bytes[2];
                byteArr[1] = bytes[3];
                byteArr[2] = bytes[4];
                byteArr[3] = bytes[5];
                int shval = BitConverter.ToInt32(byteArr);

                startIndex += 6;
                if (startIndex + 2 + shval > bytes.Count)
                    return res;

                //取出中间数据，校验验证码
                ushort crc = (ushort)CRC.CRC16(bytes, startIndex, shval);

                byteArr = new byte[2];
                for (int i = startIndex + shval; i < startIndex + shval + 2; i++)
                    byteArr[i - startIndex - shval] = bytes[i];
                ushort dataCrc = BitConverter.ToUInt16(byteArr);

                if (crc != dataCrc)
                    return null;
                else
                {
                    res = new List<byte>();
                    endIndex = startIndex + shval + 2;
                    for (int i = startIndex - 6; i < startIndex + shval + 2; i++)
                        res.Add(bytes[i]);
                }
            }
            catch (Exception)
            {
                throw;
            }
            return res;
        }
    }
}
